ext.define('extension.mutations', function() {

var utils = extension.utils;
var messages = extension.messages;
var tags = 'object,embed';
var currentDocument = document.documentElement;
var htmlObserver = new window.MutationObserver(onHTMLMutation);
var documentObserver = new window.MutationObserver(onDocumentMutation);

function collectFlashElements(element) {
    var result = [], elms = element.querySelectorAll(tags);
    for (var i = 0; i < elms.length; i++) {
        if (utils.isValidPlugin(elms[i]))
            result.push(elms[i]);
    }
    return result;
}

function concatFlashElements(array, element) {
    if (utils.isElement(element)) {
        if (utils.isValidPlugin(element))
            return array.push(element);
        return Array.prototype.push.apply(array, collectFlashElements(element));
    }
}

function removeArrayDuplicate(item, position, array) {
    return array.indexOf(item) == position;
}

function onInsert(element) {
    messages.send('insert', element);
}

function onRemove(element) {
    messages.send('remove', element);
}

function onHTMLMutation(records) {
    var addedNodes = [], removedNodes = [];
    for (var i = 0, nodes; i < records.length; i++) {
        nodes = records[i].addedNodes;
        for (var j = 0; j < nodes.length; j++) {
            if (nodes[j].parentNode != null)
                concatFlashElements(addedNodes, nodes[j]);
        }
        nodes = records[i].removedNodes;
        for (var j = 0; j < nodes.length; j++) {
            if (nodes[j].parentNode == null)
                concatFlashElements(removedNodes, nodes[j]);
        }
    }
    addedNodes.filter(removeArrayDuplicate).forEach(onInsert);
    removedNodes.filter(removeArrayDuplicate).forEach(onRemove);
}

function onDocumentMutation(records) {
    if (currentDocument !== document.documentElement) {
        var prevDoc = currentDocument;
        currentDocument = document.documentElement;
        messages.send('document-replaced', prevDoc);
    }
}

function connectObserver() {
    htmlObserver.observe(currentDocument,
        {'childList': true, 'subtree': true});
}

function disconnectObserver() {
    htmlObserver.disconnect();
}

function scanDocument() {
    collectFlashElements(currentDocument).forEach(onInsert);
}

return {
    bind: function() {
        documentObserver.observe(document, {'childList': true});
    },
    observe: connectObserver,
    disconnect: disconnectObserver,
    scan: scanDocument
};

});
